package middleware.p2p;

import java.util.LinkedList;

import middleware.p2p.log.MiddlewareLogEvent;
import middleware.p2p.log.MiddlewareMonitorEvent;
import middleware.p2p.log.VectorClock;

import org.apache.log4j.Logger;

/**
 *	Classe implementante il Manager. 
 *	Il Manager si occupa di gestire i Workflow e di appoggiarsi al Discovery per assegnare un Worker ad
 *	ogni Task del Workflow.
 *	@see	Workflow
 *	@see 	Discovery
 */
public class Manager extends Thread implements AbstractDispatcher {
	/**
	 *	Lista interna di Activity.
	 */
    private LinkedList<Activity> activities;
    /**
     * 	Coda di Workflow da cui estrarre i Workflow da gestire.
     */
    private WFQueueManager wfqm;
    /**
     * 	Servizio di Discovery per trovare i Worker necessari all'esecuzione dei Task.
     */
    private WorkerDiscovery workerDiscovery;
    /**
     * 	Costruttore principale del Manager
     * 	@param 	WFQM 	Coda dal quale estrarre i Workflow
     * 	@param 	wD 		WorkerDiscovery per trovare i Worker.
     */
    public Manager(WFQueueManager WFQM, WorkerDiscovery wD){    	
    	this.activities=new LinkedList<Activity>();
        this.wfqm = WFQM;
        this.workerDiscovery = wD;
    }
    /**
     * 	Costruttore di default della classe.
     * 	Questo costruttore imposta la coda e il discovery a null.  
     */
    public Manager(){
        this(null,null);
    }
    /**
     * 	Metodo per ottenere un array contenente tutte le activity del manager.
     * 	@return L'array di Activity del manager.
     */
    public Activity[] getActivities() {
        return activities.toArray(new Activity[activities.size()]);
    }
    /**
     * 	Metodo per aggiungere un array di Activity al Manager.
     * 	@param	val	Array di valori da inserire nel Manager.
     */
    public void setActivities(Activity[] val) {
        this.activities.clear();
        for(Activity act: val) {
            activities.add(act);
        }
    }
    /**
     * 	Metodo che si occupa del recupero del workflow da schedulare
     * 	Il metodo inizia estrendo un workflow dalla coda e per ogni task usa il discovery per trovare il giusto worker
     * 	per eseguirlo; i worker trovati vengono inseriti in una WorkerNetwork. Infine viene assemblata una activity che
     * 	viene schedulata.
     */
    public void run(){
    	Logger log=Logger.getLogger(this.getClass());
    	VectorClock logLocalClock=CORE.getLogVectorClock().addEventToClock(CORE.getComponentId()); 
		log.info(new MiddlewareLogEvent(logLocalClock,"Manager started."));
    	while (true) {			
			log.debug("Waiting for dequeueing a Workflow...");
			MiddlewareMonitorEvent monitorEvent=new MiddlewareMonitorEvent();
		    Workflow wf = wfqm.dequeue(monitorEvent);
		    logLocalClock=CORE.getLogVectorClock().addEventToClock(CORE.getComponentId());
		    VectorClock monitorLocalClock=CORE.getMonitorVectorClock().addEventToClock(CORE.getComponentId());
		    log.info(new MiddlewareLogEvent(logLocalClock,"Workflow "+wf.getID()+" extracted from the queue."));
		    monitorEvent.setVclock(monitorLocalClock);
		    monitorEvent.setComponentID(CORE.getComponentId());
		    Logger.getLogger(CORE.monitorWFQueueName).info(monitorEvent.getQueueContent());
		    Logger.getLogger(CORE.monitorWFQueueStatusName).info(monitorEvent);
		    TaskDescriptor [] taskDescriptorList = wf.getTasks();        
		    WorkerNetwork workerNetwork = new WorkerNetwork();
		    log.debug("WorkerNetwork created");
		    for(TaskDescriptor td : taskDescriptorList){
		    	Address tAddr=workerDiscovery.getWorker(td);        	
		    	workerNetwork.addWorker(tAddr);
		    	log.debug("Worker "+tAddr.getIP()+":"+tAddr.getPort()+" associated with the Task "+td.getID()+" ("+td.getCommand()+")");
		    }
		    Activity activity = new Activity();
		    activities.add(activity);
		    activity.setWf(wf);
		    activity.setSbn(workerNetwork);
		    activity.sheduleActivity();
    	}
    }
}